//
// Created by changlei on 2021/11/4.
//

import UIKit
import MJRefresh
import WebKit


fileprivate let spacing = 8.0
fileprivate let radius = 8.0
fileprivate let pageSize = 20
fileprivate let cellId = "MovieCell"
fileprivate let separatorId = "MovieCellSeparator"

class HomeController: BaseController {
    private var movies: [Movie] = []
    private var nextPage: Int = 0
    private var movieType: MovieType = .scenario
    private var task: Task<Any, Never>?

    lazy private var titleView: TitleView = {
        TitleView(target: self, action: #selector(onMovieType))
    }()
    lazy private var tableView: UITableView = {
        let uiTableView = UITableView(frame: .zero, style: .plain)
        uiTableView.backgroundColor = .clear
        uiTableView.separatorStyle = .none
        uiTableView.keyboardDismissMode = .onDrag
        uiTableView.dataSource = self
        uiTableView.delegate = self
        uiTableView.register(MovieCell.self, forCellReuseIdentifier: cellId)
        uiTableView.register(MovieSeparatorCell.self, forCellReuseIdentifier: separatorId)
        return uiTableView
    }()
    lazy private var header: MJRefreshHeader = {
        let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refresh)
        refreshHeader.setTitle("正在刷新", for: .idle)
        refreshHeader.setTitle("松开刷新", for: .pulling)
        refreshHeader.setTitle("正在刷新", for: .willRefresh)
        refreshHeader.setTitle("正在刷新", for: .refreshing)
        refreshHeader.lastUpdatedTimeLabel?.isHidden = true
        return refreshHeader
    }()
    lazy private var footer: MJRefreshFooter = {
        let refreshFooter = MJRefreshAutoGifFooter(refreshingBlock: loadNext)
        refreshFooter.setTitle("上拉加载", for: .idle)
        refreshFooter.setTitle("上拉加载", for: .pulling)
        refreshFooter.setTitle("上拉加载", for: .willRefresh)
        refreshFooter.setTitle("正在加载…", for: .refreshing)
        refreshFooter.setTitle("我是有底线的", for: .noMoreData)
        return refreshFooter
    }()
    lazy private var searchController: UISearchController = {
        let uiSearchController = UISearchController(searchResultsController: SearchResultsController())
        uiSearchController.searchResultsUpdater = self
        uiSearchController.delegate = self
        return uiSearchController
    }()

    override var title: String? {
        get {
            titleView.text
        }
        set {
            titleView.text = newValue
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "豆瓣\(movieType.name)电影"
        navigationItem.titleView = titleView
        navigationItem.searchController = searchController
        navigationItem.hidesSearchBarWhenScrolling = false

        view.addSubview(tableView)
        tableView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        tableView.mj_header = header
        header.beginRefreshing()
    }

    @objc func onMovieType() {
        let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        for movieType in MovieType.allCases {
            controller.addAction(UIAlertAction(title: movieType.name, style: .default) { action in
                self.movieType = movieType
                self.title = "豆瓣\(movieType.name)电影"
                self.header.beginRefreshing()
            })
        }
        controller.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(controller, animated: true)
    }

    func refresh() {
        task?.cancel()
        task = Task(operation: onRefresh)
    }

    func loadNext() {
        task?.cancel()
        task = Task(operation: onLoadNext)
    }

    @Sendable func onRefresh() async {
        await onRequest(0, pageSize)
    }

    @Sendable func onLoadNext() async {
        await onRequest(nextPage, pageSize)
    }

    @Sendable func onRequest(_ currentPage: Int, _ pageSize: Int) async {
        let movies = await Api.getTop250(type: movieType, currentPage: currentPage, pageSize: pageSize) ?? []
        nextPage = currentPage
        DispatchQueue.main.async {
            self.header.endRefreshing()
            self.footer.endRefreshing()
            let isFirst = currentPage == 0
            let hasNext = movies.count >= pageSize
            if hasNext {
                self.tableView.mj_footer = self.footer
                self.nextPage += 1
            } else {
                if isFirst {
                    self.tableView.mj_footer = nil
                } else {
                    self.footer.endRefreshingWithNoMoreData()
                }
            }
            if isFirst {
                self.movies.removeAll()
            }
            self.movies.append(contentsOf: movies)
            self.tableView.reloadData()
        }
    }
}

extension HomeController: UITableViewDataSource, UITableViewDelegate {
    func isSpacing(_ indexPath: IndexPath) -> Bool {
        indexPath.row % 2 == 0
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        movies.count * 2 + 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if isSpacing(indexPath) {
            return tableView.dequeueReusableCell(withIdentifier: separatorId) as! MovieSeparatorCell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: cellId) as! MovieCell
            cell.setMovie(movies[indexPath.row / 2]) { interaction, configuration, animator in
                self.tableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
            }
            return cell
        }
    }

    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if isSpacing(indexPath) {
            return spacing
        } else {
            return UITableView.automaticDimension
        }
    }

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if isSpacing(indexPath) {
            return
        }
        let movie = movies[indexPath.row / 2]
        navigationController?.pushViewController(movie.detailController, animated: true)
    }

    public func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        if isSpacing(indexPath) {
            return nil
        }

        let movie = movies[indexPath.row / 2]
        return movie.barrenContextMenuConfiguration
    }

    public func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        animator.addCompletion {
            var controller: UIViewController!
            if let previewController = animator.previewViewController {
                controller = previewController
            } else {
                controller = (configuration.identifier as! Viewable).detailController
            }
            self.navigationController?.pushViewController(controller, animated: true)
            if let controller = controller as? ViewableChromeController {
                controller.isReadOnly = false
            }
        }
    }

    public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        if isSpacing(indexPath) {
            return nil
        }
        let movie = movies[indexPath.row / 2]
        return movie.leadingSwipeActionsConfiguration
    }

    public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        if isSpacing(indexPath) {
            return nil
        }
        let movie = movies[indexPath.row / 2]
        return movie.trailingSwipeActionsConfiguration
    }
}

extension HomeController: UISearchResultsUpdating, UISearchControllerDelegate {
    func updateSearchResults(for searchController: UISearchController) {
        let resultsController = searchController.searchResultsController as! SearchResultsController
        resultsController.query(text: searchController.searchBar.text)
    }

    public func willPresentSearchController(_ searchController: UISearchController) {
        header.state = MJRefreshState.idle
        header.isHidden = true
    }

    public func willDismissSearchController(_ searchController: UISearchController) {
        header.isHidden = false
    }
}

class MovieCell: UITableViewCell {
    private let container: UIView = {
        let uiView = UIView()
        uiView.layer.cornerRadius = radius
        uiView.clipsToBounds = true
        uiView.layer.borderColor = UIColor.separator.cgColor
        uiView.layer.borderWidth = 0.2
        uiView.backgroundColor = .systemBackground
        return uiView
    }()
    private let avatar: CoverView = {
        CoverView()
    }()
    private let title: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .label
        uiLabel.font = .systemFont(ofSize: 15, weight: .medium)
        uiLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        uiLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
        return uiLabel
    }()
    private let playable: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .systemGreen
        uiLabel.font = .systemFont(ofSize: 14, weight: .medium)
        uiLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
        uiLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        return uiLabel
    }()
    private let actors: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .label.withAlphaComponent(0.8)
        uiLabel.font = .systemFont(ofSize: 12, weight: .regular)
        return uiLabel
    }()
    private let actions: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .label.withAlphaComponent(0.8)
        uiLabel.font = .systemFont(ofSize: 12, weight: .regular)
        return uiLabel
    }()
    private let scores: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .systemOrange
        uiLabel.font = .systemFont(ofSize: 12, weight: .medium)
        return uiLabel
    }()
    private let votes: UILabel = {
        let uiLabel = UILabel()
        uiLabel.textColor = .label.withAlphaComponent(0.8)
        uiLabel.font = .systemFont(ofSize: 12, weight: .medium)
        return uiLabel
    }()

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override init(style: CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        selectionStyle = .none

        contentView.addSubview(container)
        container.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(NSDirectionalEdgeInsets(top: 0, leading: spacing, bottom: 0, trailing: spacing))
        }

        container.addSubview(avatar)
        container.addSubview(title)
        container.addSubview(playable)
        container.addSubview(actors)
        container.addSubview(actions)
        container.addSubview(scores)
        container.addSubview(votes)

        avatar.snp.makeConstraints { make in
            make.width.equalTo(80)
            make.height.equalTo(avatar.snp.width).multipliedBy(4.0 / 3.0).priority(.high)
            make.top.leading.equalToSuperview().offset(spacing)
            make.bottom.lessThanOrEqualToSuperview().offset(-spacing)
        }
        title.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(spacing)
            make.leading.equalTo(avatar.snp.trailing).offset(spacing)
        }
        playable.snp.makeConstraints { make in
            make.centerY.equalTo(title)
            make.leading.equalTo(title.snp.trailing).offset(spacing)
            make.trailing.lessThanOrEqualToSuperview().offset(-spacing)
        }
        actors.snp.makeConstraints { make in
            make.top.equalTo(title.snp.bottom).offset(12)
            make.leading.equalTo(title)
            make.trailing.equalToSuperview().offset(-spacing)
        }
        actions.snp.makeConstraints { make in
            make.top.equalTo(actors.snp.bottom).offset(2)
            make.leading.trailing.equalTo(actors)
        }
        scores.snp.makeConstraints { make in
            make.top.equalTo(actions.snp.bottom).offset(12)
            make.leading.equalTo(actions)
            make.bottom.lessThanOrEqualToSuperview().offset(-spacing)
        }
        votes.snp.makeConstraints { make in
            make.top.equalTo(scores)
            make.leading.equalTo(scores.snp.trailing).offset(spacing)
            make.trailing.lessThanOrEqualToSuperview().offset(-spacing)
        }
    }

    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
        super.setHighlighted(highlighted, animated: animated)
        UIView.animate(withDuration: 0.15) {
            self.transform = highlighted ? CGAffineTransform.identity.scaledBy(x: 1.02, y: 1.02) : CGAffineTransform.identity
        }
    }

    override func updateConfiguration(using state: UICellConfigurationState) {
        super.updateConfiguration(using: state)
        var configuration = UIBackgroundConfiguration.clear()
        configuration.backgroundColor = .clear
        configuration.cornerRadius = radius
        let safeAreaInsets = AppDelegate.default.window?.safeAreaInsets
        var edgeInsets = NSDirectionalEdgeInsets()
        edgeInsets.leading = spacing + (safeAreaInsets?.left ?? 0)
        edgeInsets.trailing = spacing + (safeAreaInsets?.right ?? 0)
        configuration.backgroundInsets = edgeInsets
        backgroundConfiguration = configuration
    }

    func setMovie(_ value: Movie, willPerformPreviewAction: WillPerformPreviewAction? = nil) {
        avatar.setImage(with: value.coverUrl)
        avatar.setInteraction(value.contextMenuConfiguration) { interaction, configuration, animator in
            if let willPerformPreviewAction = willPerformPreviewAction {
                willPerformPreviewAction(interaction, configuration, animator)
            }
        }
        title.text = value.title
        playable.text = "[\(value.isPlayable ? "可播放" : "不可播放")]"
        actors.text = "主演：\(value.actors.joined(separator: "/"))"
        var actions: [String] = []
        actions.append(Dates.tryFormat(value.releaseDate, pattern: "yyyy") ?? value.releaseDate)
        actions.append(contentsOf: value.regions)
        actions.append(contentsOf: value.types)
        self.actions.text = actions.joined(separator: "/")
        scores.text = value.score
        votes.text = "\(value.voteCount)人评价"
    }
}

class MovieSeparatorCell: UITableViewCell {
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override init(style: CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        selectionStyle = .none
        backgroundColor = .clear
    }
}

class TitleView: UIView {
    private let normalAlpha = 1.0
    private let touchedAlpha = 0.3

    lazy private var titleLabel: UILabel = {
        let uiLabel = UILabel()
        uiLabel.font = .systemFont(ofSize: 17, weight: .bold)
        uiLabel.textColor = .label
        return uiLabel
    }()
    lazy private var suffixIcon: UIImageView = {
        let uiImageView = UIImageView()
        uiImageView.image = UIImage(systemName: "arrowtriangle.down.fill")
        uiImageView.tintColor = .label
        uiImageView.sizeToFit()
        return uiImageView
    }()

    var text: String? {
        get {
            titleLabel.text
        }
        set {
            titleLabel.text = newValue
        }
    }

    convenience init(title: String? = nil, target: Any?, action: Selector?) {
        self.init()
        addSubview(titleLabel)
        addSubview(suffixIcon)

        titleLabel.snp.makeConstraints { make in
            make.top.leading.bottom.equalToSuperview()
        }
        suffixIcon.snp.makeConstraints { make in
            make.leading.equalTo(titleLabel.snp.trailing).offset(4)
            make.centerY.equalTo(titleLabel)
            make.size.equalTo(CGSize(width: 14, height: 14))
            make.trailing.equalToSuperview()
        }

        let gestureRecognizer = UITapGestureRecognizer(target: target, action: action)
        gestureRecognizer.isEnabled = true
        isUserInteractionEnabled = true
        addGestureRecognizer(gestureRecognizer)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        onTouched()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        onTouched(touches.contains(where: { touch in point(inside: touch.location(in: self), with: event) }))
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        onTouched(false)
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesCancelled(touches, with: event)
        onTouched(false)
    }

    func onTouched(_ touched: Bool = true) {
        UIView.animate(withDuration: 0.15) { [self] in
            alpha = touched ? touchedAlpha : normalAlpha
        }
    }
}